home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1997 September / Macworld (1997-09).dmg / Shareware World / Utilities / Text Processing / Alpha / Tcl / Modes / perl.tcl < prev    next >
Text File  |  1997-04-02  |  50KB  |  1,672 lines

  1. #############################################################################
  2. # MacPerl.tcl
  3. # -----------
  4. #
  5. # This is a set of routines that allow Alpha to act as a front end for the
  6. # standalone MacPerl application and that allow Perl scripts to be used as 
  7. # text filters in Alpha.  These functions are accessed through a special 
  8. # MacPerl menu.
  9. #
  10. # The features of this package are explained in the file "MacPerl Help",
  11. # accessible from the Help menu.
  12. #
  13. #############################################################################
  14. #
  15. # If you don't already have MacPerl, it's available by anonymous ftp from
  16. # the umich site
  17. #
  18. #   mac.archive.umich.edu    [141.211.165.34]    mac/development/languages
  19. #
  20. # and its mirrors.  Also, MacPerl's home site is 
  21. #
  22. #   ftp.switch.ch            [130.59.1.40]        software/mac/src/mpw_c
  23. #
  24. # MacPerl was written (ported to the Mac) by 
  25. #        Matthias Neeracher <neeri@iis.ee.ethz.ch> , and
  26. #        Tim Endres <time@ice.com>.
  27. #
  28. #############################################################################
  29. # Author: Tom Pollard <pollard@schrodinger.com>
  30. #
  31. # Contributors: Dan Herron     <herron@cogsci.ucsd.edu>
  32. #               David Schooley <schooley@ee.gatech.edu>
  33. #               Vince Darley   <vince@das.harvard.edu>
  34. #               Martijn Koster <m.koster@nexor.co.uk>
  35. #
  36. # Version History:
  37. #
  38. # 3.0  4/97  -  MacPerl interactions don't depend on MacPerl app name anymore
  39. #               Fixed bug with perlFileAsFilter ($scriptStart uninitialized)
  40. # 2.9  3/97  -  Fixed bug in command-dbl-click help lookup for Perl5 mode
  41. # 2.8  2/97  -  Added Quick-Save commands in new submenu [Dan Herron]
  42. #               "Save As CGI" finally works.
  43. # 2.7  2/97  -  Comments before "#!/bin/perl" no longer confuse 'gotoPerlError'
  44. # 2.6  2/97  -  Added electricPerlLeft and electricPerlRight - [David Schooley]
  45. # 2.51 1/96  -  Fixed problem w/ "Tell MacPerl:Save As..."
  46. # 2.5  1/96  -  Colorization and cmd-dbl-click modified to support Perl 5 docs
  47. # 2.41 7/95  -  Minor tweaks
  48. # 2.4  7/95  -  Fixed bugs affecting running unsaved scripts and error handling
  49. # 2.3  7/95  -  Minor tweaks and code rearrangement.
  50. # 2.2  6/95  -  Text filters act only on current line if "Apply to Buffer" is
  51. #                  false and no text has been selected.
  52. #               Bug fix in error-marking for scripts sent as AppleEvent params.
  53. #               Cmd-dbl-clicking a function call jumps to function, if
  54. #                  defined in the same file.
  55. # 2.1  6/95  -  Cmd-dbl-clicking a 'require'd filename opens the file.
  56. # 2.0  6/95  -  Minor bug fixes (incl. keyword decapitalization)
  57. #               Alpha 6.0b17 compatibility updates.
  58. #               Text Filters folder is settable from the App Paths menu now.
  59. # 1.9  5/95  -  Cmd-dbl-clicking Perl keywords and special variables displays
  60. #                  the man page info.
  61. # 1.81 4/95  -  one very minor Alpha compatibility update (winInfo->getWinInfo).
  62. # 1.8  4/95  -  Menu reorganized somewhat.
  63. #               Text Filters folder can now be anywhere.
  64. #               "ApplyToBuffer" flag ignored if text has been selected.
  65. #               Bug fixes.
  66. # 1.7  1/95  -  Updated to take advantage of MacPerl 4.1.4 AppleEvent features:
  67. #                1) Text filters use 'batch' doScript (.: STDOUT file obsolete)
  68. #                2) Filter scripts sent as doScript params (.: SCRIPT file obsolete)
  69. #                3) "Save As Droplet" and "Save as Runtime" commands added.
  70. #               Errors generated in 'require'd files are now displayed correctly
  71. # 1.6 10/94  -  "UseDebugger" flag added (forces scripts to run under debugger).
  72. #               Key bindings added for some menu commands.
  73. #               "perlDoScript{,2,3}" procs consolidated into a single proc.
  74. #               "saveAndRun" option added.
  75. #               Command-line args now parsed into units more correctly, in
  76. #                   particular, quoted file names aren't broken up.
  77. #               "Close Output Window" added to "Tell MacPerl" menu.
  78. #               Updated for Alpha 5.98 to load when menu is inserted.
  79. #               The error messages window is now recycled.
  80. #               "perlRecycleOutput" recycles output window.
  81. #               Minor bug fixes.
  82. # 1.5  9/94  -  MacPerl menu rearranged somewhat.
  83. #               Explicit "Get Output Window" command added to menu.
  84. #               Reading "#!" line for args is incompatible w/ standard,
  85. #                   so it's been dropped.
  86. #               Only scan the first 40 output lines for error messages (faster)
  87. #                "wrapFilterScript" no longer opens STDIN
  88. #               Text filters may now use command-line args
  89. #               STDIN for text filters passed as explicit cmd-line arg 
  90. # 1.4  9/94  -  The "#!" line of every script is read for command-line args,
  91. #                    which are passed explicitly to MacPerl with the script.
  92. #                "PromptForArgs" menu flag added.
  93. #                "perlCmdlineArgs" modeVar holds default command-line args.
  94. #                Scripts are sent using custom "perlDoScript2" proc, which
  95. #                    allows passing of explicit command-line args.
  96. # 1.3  9/94  -  When any script generates a compilation error, the file 
  97. #                    containing the script is brought up with the offending 
  98. #                    line highlighted; all error output is also written to
  99. #                    a "Perl Error Messages" window.
  100. #                'repeatLastFilter' runs again the last text-filter script used.
  101. #                'perlLastFilter' modeVar holds pathname of last filter.
  102. #                Menu flags now mirrored as modeVars, so they can be saved and
  103. #                    restored between sessions.
  104. #                Minor bug fixes.
  105. # 1.2  8/94  -  'retrieveOutput' and 'autoSwitch' flags added.
  106. #                'openInMacperl' added.
  107. #                MacPerl output window now closed before new scripts are sent.
  108. #                Filters now abort if there are compilation errors, and
  109. #                MacPerl diagnostic output retrieved and displayed in Alpha.
  110. # 1.1  8/94  -  'quitMacperl' added.
  111. #               perl-mode file-marking updated for Alpha 5.90
  112. #               Simplified installation via 'loadMacperl'(Pete Keleher). 
  113. # 1.0  7/94  -  perl-mode setup updated for Alpha 5.85:
  114. #                    keyword colorization supported
  115. #                    custom file-marking added
  116. #               #! lines in filter scripts now handled correctly 
  117. #               Workarounds installed for AppleEvent bug in MacPerl 4.1.3
  118. # 0.9  3/94  -  perl-mode stuff added, and
  119. #               highlighted 'Perl commands' file (man page) prepared
  120. #               minor bug fixes, too
  121. # 0.8  3/94  -  flags are now check-marked
  122. # 0.7  3/94  -  nested Text Filters folder now supported
  123. #               menu format modified somewhat
  124. # 0.6  3/94  -  'applyToBuffer' flag added
  125. #               scripts in Alpha buffers can now be used as filters 
  126. # 0.5  2/94  -  'filters', 'open special' submenu added
  127. #               'overwrite' flag added
  128. # 0.2  1/94  -  menu support added (Martijn Koster <m.koster@nexor.co.uk>)
  129. #               'execute selection', 'execute buffer' commands added
  130. # 0.1  9/93  -  text filter functionality created
  131. #                  
  132. ##############################################################################
  133. #
  134. proc dummyPerl {} {
  135. }
  136.  
  137. #############################################################################
  138. #  Default settings for the Perl menu flags  
  139. #
  140. set perlDefault(perlUseDebug) 0
  141. set perlDefault(perlGetOutput) 1
  142. set perlDefault(perlAutoSwitch) 1
  143. set perlDefault(perlOverwrite) 0
  144. set perlDefault(perlUsebuffer) 1
  145. set perlDefault(perlPromptArgs) 0
  146. set perlDefault(perlRecycleOutput) 0
  147. set perlDefault(perlPrevScript) {*startup*}
  148. set perlDefault(perlCmdlineArgs) {}
  149. set perlDefault(perlVersion) {4}
  150.  
  151. if {![info exists perlFilterPath]} {
  152.     set perlFilterPath "$HOME:Tcl:UserCode:Text Filters:"
  153. }
  154.  
  155. foreach var [array names perlDefault] {
  156.     if (![info exists PerlmodeVars($var)]) { 
  157.         set $var $perlDefault($var) 
  158.     } else {
  159.         set $var $PerlmodeVars($var) 
  160.     }
  161. }
  162. unset perlDefault
  163.  
  164. ##############################################################################
  165. # Make duplicate copies of these variables as modeVars, taking responsibility
  166. # for keeping the two sets consistent (argh!)
  167. #
  168. # (Maybe it's OK now to let them _just_ be modeVars, and not also ordinary
  169. # variables?)
  170. #
  171.  
  172. newModeVar Perl perlUseDebug $perlUseDebug 1
  173. newModeVar Perl perlGetOutput $perlGetOutput 1
  174. newModeVar Perl perlAutoSwitch $perlAutoSwitch 1
  175. newModeVar Perl perlOverwrite $perlOverwrite 1
  176. newModeVar Perl perlUsebuffer $perlUsebuffer 1
  177. newModeVar Perl perlPromptArgs $perlPromptArgs 1
  178. newModeVar Perl perlRecycleOutput $perlRecycleOutput 1
  179.  
  180. newModeVar Perl perlLastFilter $perlPrevScript 0
  181. newModeVar Perl perlCmdlineArgs $perlCmdlineArgs 0
  182.  
  183. ##############################################################################
  184. # Other Perl-mode variable definitions
  185. #
  186. newModeVar Perl elecRBrace        {0} 1
  187. newModeVar Perl elecLBrace        {1} 1
  188. newModeVar Perl electricSemi    {0} 1
  189. newModeVar Perl electricTab        {1} 1
  190. newModeVar Perl electricReturn    {1} 1
  191. newModeVar Perl wordBreak        {(\$)?\w+} 0
  192. newModeVar Perl prefixString    {# } 0
  193. newModeVar Perl wordWrap        {0} 1
  194. newModeVar Perl funcExpr        {^sub *([+-a-zA-Z0-9]+)} 0
  195. newModeVar Perl wordBreakPreface        {[^a-zA-Z0-9_\$]} 0
  196. newModeVar Perl autoMark    1    1
  197. newModeVar Perl stringColor    green    0
  198.  
  199. newModeVar Perl perlVersion $perlVersion 0
  200.  
  201.  
  202. ##############################################################################
  203. # Miscellaneous definitions
  204. #
  205. set perlErrorWindow {* Perl Error Messages *}
  206. set perlOutputWindow {* Perl Output *}
  207. set interpPat {(#![     !-~]*)}
  208.  
  209. set perlFilterMenu "textFilters"
  210.  
  211. set modeCode ":Tcl:Modes:perl${perlVersion}.tcl"
  212. if {[catch {source $HOME$modeCode}]} {
  213.     alertnote "Couldn't load the Perl-mode colorization file \"$modeCode\".  Contact the maintainer."
  214. }
  215.  
  216. #############################################################################
  217. #  Return paths to standard files, based on the path to MacPerl:
  218. #
  219. proc macperlFolder {} {
  220.    set name [nameFromAppl McPL]
  221.    regexp {(.*):([^:]*)} $name pathname dirname filename
  222.    return ${dirname}:
  223. }
  224.  
  225. proc stdinPath {} {
  226.    return [macperlFolder]STDIN
  227. }
  228.  
  229. proc scriptPath {} {
  230.    return [macperlFolder]SCRIPT
  231. }
  232.  
  233. #############################################################################
  234. # Define the dummy proc that will be called when the perl menu
  235. # is first inserted into the menubar
  236. #
  237. proc perlMenu {} {}
  238.  
  239. #############################################################################
  240. #  Build the perl menu
  241. #            
  242. set perlMenu "•132"
  243. set perlOptsMenu "generalOptions"
  244. set filtOptsMenu "filterOptions"
  245.  
  246. menu -n $perlMenu [ concat {
  247.         "/'<Umacperl"
  248.         {menu -m -n "tellMacperl..." -p perlTellProc {
  249.            "/O<UOpen This File"
  250.            "Save As Droplet"
  251.            "Save As Runtime"
  252.            "Save As CGI"
  253.             "(-"
  254.            "Get Output Window"
  255.            "Close Output Window"
  256.            "Quit"
  257.            }
  258.         } 
  259.         {menu -m -n "Quick Save As..." -p perlSaveProc {
  260.            "Droplet"
  261.            "Runtime"
  262.            "CGI"
  263.            }
  264.         } 
  265.         {menu -m -n help -p perlHelpProc {
  266.             "MacPerl Mode"
  267.             "Mac Specifics"
  268.             "Perl4 Manual"
  269.             "Perl5 Manual"
  270.         }}
  271.         "(-"
  272.         "runTheSelection"
  273.         "/R<UrunTheBuffer"
  274.         "/R<B<OsaveAndRun"
  275.         "runAFile"
  276.         "(-"
  277.     } [list [list menu -n $perlFilterMenu {}]] {
  278.        "selectBufferAsFilter"
  279.        "selectFileAsFilter"
  280.        "/F<UrepeatLastFilter"
  281.        "(-" 
  282.     } [list [list menu -n $perlOptsMenu {}]] {
  283.     } [list [list menu -n $filtOptsMenu {}]] {
  284.     } ]
  285.  
  286. enableMenuItem $perlMenu perlDebugWindow 0
  287. enableMenuItem "tellMacperl..." "Save As CGI" 1
  288.  
  289. if {$perlPrevScript == {} || $perlPrevScript == {*startup*}} {
  290.     enableMenuItem $perlMenu repeatLastFilter 0
  291. }
  292.  
  293. # General Perl-menu options menu
  294. #
  295. menu -n $perlOptsMenu {
  296.     "retrieveOutput"
  297.     "autoSwitch"
  298.     "promptForArgs"
  299.     "useDebugger"
  300.     }    
  301. markMenuItem $perlOptsMenu useDebugger $perlUseDebug
  302. markMenuItem $perlOptsMenu retrieveOutput $perlGetOutput
  303. markMenuItem $perlOptsMenu autoSwitch $perlAutoSwitch
  304. markMenuItem $perlOptsMenu promptForArgs $perlPromptArgs
  305.  
  306. # Text Filter options menu
  307. #
  308. menu -n $filtOptsMenu {
  309.     "applyToBuffer"
  310.     "overwriteSelection"
  311.     "(-"
  312.     "textFiltersFolder"
  313.     "rebuildFilterMenu"
  314.     }    
  315. markMenuItem $filtOptsMenu overwriteSelection $perlOverwrite
  316. markMenuItem $filtOptsMenu applyToBuffer $perlUsebuffer
  317.  
  318.  
  319. #############################################################################
  320. #  Build a submenu of "preattached" Perl filters using the names of the 
  321. #  scripts in the Text Filters directory.  Called whenever Text Filters
  322. # folder is reassigned.
  323. #
  324. proc rebuildFilterMenu {{args}} {
  325.     global perlFilters perlFilterMenu perlFilterPath
  326.     global $perlFilterMenu
  327.     
  328.     eval [buildSubMenu [list $perlFilterPath] $perlFilterMenu textFiltersProc perlFilters]
  329. }
  330.  
  331. rebuildFilterMenu
  332.  
  333. #############################################################################
  334. # Use variable tracing to keep global vars and modeVars consistent.
  335. #
  336. trace variable PerlmodeVars(perlUseDebug) w shadowPerl
  337. trace variable PerlmodeVars(perlOverwrite) w shadowPerl
  338. trace variable PerlmodeVars(perlUsebuffer) w shadowPerl
  339. trace variable PerlmodeVars(perlGetOutput) w shadowPerl
  340. trace variable PerlmodeVars(perlAutoSwitch) w shadowPerl
  341. trace variable PerlmodeVars(perlPromptArgs) w shadowPerl
  342. trace variable PerlmodeVars(perlLastFilter) w shadowPerl
  343. trace variable PerlmodeVars(perlCmdlineArgs) w shadowPerl
  344. trace variable PerlmodeVars(perlRecycleOutput) w shadowPerl
  345. trace variable PerlmodeVars(perlVersion) w shadowPerl
  346.  
  347. # perlFilterPath is now just a regular variable, set from the App Paths submenu
  348. trace variable perlFilterPath w rebuildFilterMenu
  349.  
  350. # ShadowPerl sets the global vars when the mode vars are modified and
  351. # keeps the menu checkmarked correctly.
  352. #
  353. proc shadowPerl {name1 name2 op} {
  354.     global HOME perlMenu perlOptsMenu filtOptsMenu
  355.     global perlOverwrite perlUsebuffer perlGetOutput perlAutoSwitch
  356.     global perlPromptArgs perlPrevScript perlCmdlineArgs perlUseDebug
  357.     global PerlmodeVars
  358.     if {$name1 == "PerlmodeVars" && $op == "w"} {
  359.         switch $name2 {
  360.             "perlUseDebug"    {
  361.                 set perlUseDebug $PerlmodeVars(perlUseDebug)
  362.                 markMenuItem $perlOptsMenu useDebugger $perlUseDebug
  363.              }
  364.             "perlOverwrite"    {
  365.                 set perlOverwrite $PerlmodeVars(perlOverwrite)
  366.                 markMenuItem $filtOptsMenu overwriteSelection $perlOverwrite
  367.              }
  368.             "perlUsebuffer"    {
  369.                 set perlUsebuffer $PerlmodeVars(perlUsebuffer)
  370.                 markMenuItem $filtOptsMenu applyToBuffer $perlUsebuffer
  371.              }
  372.             "perlGetOutput"    {
  373.                 set perlGetOutput $PerlmodeVars(perlGetOutput)
  374.                 markMenuItem $perlOptsMenu retrieveOutput $perlGetOutput 
  375.             }
  376.             "perlAutoSwitch" {    
  377.                 set perlAutoSwitch $PerlmodeVars(perlAutoSwitch)
  378.                 markMenuItem $perlOptsMenu autoSwitch $perlAutoSwitch 
  379.             }
  380.             "perlPromptArgs" {    
  381.                 set perlPromptArgs $PerlmodeVars(perlPromptArgs)
  382.                 markMenuItem $perlOptsMenu promptForArgs $perlPromptArgs 
  383.             }
  384.             "perlCmdlineArgs" {    
  385.                 set perlCmdlineArgs $PerlmodeVars(perlCmdlineArgs)
  386.             }
  387.             "perlRecycleOutput" {    
  388.                 set perlRecycleOutput $PerlmodeVars(perlRecycleOutput)
  389.             }
  390.             "perlVersion" {    
  391.                 set perlVersion $PerlmodeVars(perlVersion)
  392.                 set modeCode ":Tcl:Modes:perl${perlVersion}.tcl"
  393.                 if {[catch {source $HOME$modeCode}]} {
  394.                     alertnote "Couldn't load the Perl-mode colorization file \"$modeCode\".  Contact the maintainer."
  395.                 }
  396.             }
  397.             "perlLastFilter" {    
  398.                 # Don't allow perlPrevScript to be changed from the flags menu
  399.                 if {$perlPrevScript == "*startup*"} {
  400.                     set perlPrevScript $PerlmodeVars(perlLastFilter)
  401.                     enableMenuItem $perlMenu repeatLastFilter 1
  402.                 } else {
  403.                     set PerlmodeVars(perlLastFilter) $perlPrevScript 
  404.                 }
  405.             }
  406.             default {
  407.                 return
  408.             }
  409.         }
  410.     }
  411. }
  412.  
  413. #############################################################################
  414. # Menu commands
  415. #############################################################################
  416.  
  417. ############################################################################
  418. # Toggle the perl menu flags
  419. #
  420. proc retrieveOutput {} {
  421.     global perlMenu PerlmodeVars perlGetOutput modifiedModeVars
  422.     lappend modifiedModeVars [list perlGetOutput PerlmodeVars]
  423.     if {$perlGetOutput} then {
  424.         set PerlmodeVars(perlGetOutput) 0
  425.     } else {
  426.         set PerlmodeVars(perlGetOutput) 1
  427.     }
  428. }
  429.  
  430. proc useDebugger {} {
  431.     global perlMenu PerlmodeVars perlUseDebug modifiedModeVars
  432.     lappend modifiedModeVars [list  perlUseDebug PerlmodeVars]
  433.     if {$perlUseDebug} then {
  434.         set PerlmodeVars(perlUseDebug) 0
  435.     } else {
  436.         set PerlmodeVars(perlUseDebug) 1
  437.     }
  438. }
  439.  
  440. proc autoSwitch {} {
  441.     global perlMenu PerlmodeVars perlAutoSwitch modifiedModeVars
  442.     lappend modifiedModeVars [list  perlAutoSwitch PerlmodeVars]
  443.     if {$perlAutoSwitch} then {
  444.         set PerlmodeVars(perlAutoSwitch) 0
  445.     } else {
  446.         set PerlmodeVars(perlAutoSwitch) 1
  447.     }
  448. }
  449.  
  450. proc overwriteSelection {} {
  451.     global perlMenu perlOverwrite PerlmodeVars modifiedModeVars
  452.     lappend modifiedModeVars [list  perlOverwrite PerlmodeVars]
  453.     if {$perlOverwrite} then {
  454.         set PerlmodeVars(perlOverwrite) 0
  455.     } else {
  456.         set PerlmodeVars(perlOverwrite) 1
  457.     }
  458. }
  459.  
  460. proc applyToBuffer {} {
  461.     global perlMenu perlUsebuffer PerlmodeVars modifiedModeVars
  462.     lappend modifiedModeVars [list  perlUsebuffer PerlmodeVars]
  463.     if {$perlUsebuffer} then {
  464.            set PerlmodeVars(perlUsebuffer) 0
  465.     } else {
  466.            set PerlmodeVars(perlUsebuffer) 1
  467.     }
  468. }
  469.  
  470. proc promptForArgs {} {
  471.     global perlMenu perlPromptArgs PerlmodeVars modifiedModeVars
  472.     lappend modifiedModeVars [list perlPromptArgs PerlmodeVars]
  473.     if {$perlPromptArgs} then {
  474.            set PerlmodeVars(perlPromptArgs) 0
  475.     } else {
  476.            set PerlmodeVars(perlPromptArgs) 1
  477.     }
  478. }
  479.  
  480. proc textFiltersFolder {} {
  481.     global perlMenu perlFilterPath PerlmodeVars modifiedModeVars pathComments
  482.     
  483.     pathProc {} $pathComments(perlFilterPath)
  484. }
  485.  
  486. #############################################################################
  487. # Switch to MacPerl:
  488. proc macperl {} {
  489.     launchForeAppl McPL
  490. }
  491.  
  492. #############################################################################
  493. # Interact with MacPerl in some other way besides executing a script
  494. #
  495. #DTH: note addition of two lines for auto-save
  496. proc perlTellProc {menu name} {
  497.     switch -exact $name {
  498.     "Open This File"        { openInMacperl }
  499.     
  500.     "Save As Droplet"        { saveThruMacperl "droplet" }
  501.     
  502.     "Save As Runtime"        { saveThruMacperl "runtime" }
  503.     
  504.     "Save As CGI"            { saveThruMacperl "cgi" }
  505.     
  506.     "Get Output Window"        { openPerlOutput }
  507.     
  508.     "Close Output Window"    { sendCloseWinName MacPerl $perlName ;
  509.                               sendCloseWinName MacPerl "Perl Debug" }
  510.                             
  511.     "Quit"                    { quitMacperl }
  512.     }
  513. }
  514.  
  515. proc perlSaveProc {menu name} {
  516.     switch -exact $name {
  517.     "Droplet"    { saveThruMacperl "auto-droplet" }
  518.     
  519.     "Runtime"    { saveThruMacperl "auto-runtime" }
  520.  
  521.     "CGI"        { saveThruMacperl "auto-cgi" }
  522.     }
  523. }
  524.  
  525. #############################################################################
  526. # Open the current file under MacPerl.  This used to useful for saving files 
  527. # as droplets or runtime scripts.  Maybe it's still useful for something...?
  528. #
  529. proc openInMacperl {} {
  530.     if {[winDirty]} {
  531.         case [askyesno -c "Save '[lindex [winNames] 0]'?"] in {
  532.             "yes" {save}
  533.             "no" {}
  534.             "cancel" {return}
  535.         }
  536.     }
  537.     set name [launchForeAppl McPL]
  538.     sendOpenEvent -n [file tail $name] [car [winNames -f]]
  539. }
  540.  
  541. #############################################################################
  542. # Save the script in the current window as a MacPerl droplet or 
  543. # runtime script.  
  544. #
  545. proc saveThruMacperl {type} {
  546.     global ALPHA
  547.  
  548.     set name [file tail [launchBackAppl McPL]]
  549.     getWinInfo arr
  550.     if {$arr(dirty) == 1} {
  551.         case [askyesno -c "Save '[lindex [winNames] 0]' source file also?"] in {
  552.             "yes" {save}
  553.             "no" {}
  554.             "cancel" {return}
  555.         }
  556.     }
  557.     #DTH note the following "if" block which replaced what is in the new "else" block
  558.     set myName [lindex [winNames -f] 0]
  559.     if {$type == "auto-droplet" || $type == "auto-runtime"} {
  560.         if {[file extension $myName] == ".pl"} {
  561.             set destfile [AEFilename [file rootname $myName]]
  562.         } else {
  563.             set destfile [AEFilename [file rootname $myName]]
  564.         }
  565.     } elseif {$type == "auto-cgi"} {
  566.         set destfile [AEFilename "[file rootname $myName].cgi"]
  567.     } else {
  568.         set destfile [AEFilename [putfile {Save droplet as} [lindex [winNames] 0]]]
  569.     }
  570.  
  571.     set script [curlyq [getText 0 [maxPos]]]
  572.     #DTH note addition of "auto-xxx" in two lines below
  573.     if {$type == "droplet" || $type == "auto-droplet"} {
  574.         set saveType "SCPT"
  575.     } elseif {$type == "runtime" || $type == "auto-runtime"} {
  576.         set saveType "MrP7"
  577.     } elseif {$type == "cgi" || $type == "auto-cgi"} {
  578.         set saveType "'WWWΩ'"
  579.     } elseif {$type == "text"} {
  580.         set saveType "TEXT"
  581.     }
  582.     
  583.     set err [catch {eval "AEBuild -t 36000 -r \"$name\"" core save {----} [list $script] {dest:} [list $destfile] {fltp:} $saveType } reply ]
  584.     if {$err} { message "AEBuild error code $err in saveThruMacperl" }
  585.     
  586. # The following lines could be used to tell MacPerl to take the script file 
  587. # from an existing disk file and then re-save it in the desired form.
  588. #
  589. #    set srcfile "\[ [AEFilename [car [winNames -f]]] \]"
  590. #    set reply [eval "AEBuild -t 36000 -r \"$name\"" core save {----} [list $srcfile] {dest:} [list $destfile] {fltp:} $saveType ]
  591. #
  592. }
  593.  
  594. #############################################################################
  595. # Quit a running MacPerl app:
  596. proc quitMacperl {} {
  597.     foreach proc [processes] {
  598.         set sig [lindex $proc 1]
  599.         if {$sig == "McPL"} {
  600.             sendQuitEvent [lindex $proc 0]
  601.             # switchTo is necessary to keep MacPerl from blinking
  602.             switchTo [lindex $proc 0]    
  603.         }
  604.     }
  605. }
  606.  
  607. #############################################################################
  608. # Run the selection as a MacPerl script:
  609. # (No special arrangements are made to provide input or capture the output)
  610. proc runTheSelection {} {
  611.     global scriptFile scriptStart
  612.     set scriptFile [car [winNames -f]]
  613.     set scriptStart [lindex [posToRowCol [getPos]] 0]
  614.     perlExecuteScript [getSelect]
  615. }
  616.  
  617. proc runTheBuffer {} {
  618.     global scriptFile scriptStart
  619.     set scriptFile [car [winNames -f]]
  620.     set scriptStart 1
  621.     perlExecuteScript [getText 0 [maxPos]]
  622. }
  623.  
  624. proc runAFile {} {
  625.     global scriptFile scriptStart
  626.     if {! [catch {getfile "Select a Perl script"} path]} {
  627.         set scriptFile $path
  628.         set scriptStart 1
  629.         perlExecuteFile $path
  630.     }
  631. }
  632.  
  633. proc saveAndRun {} {
  634.     global scriptFile scriptStart
  635.     save
  636.     set path [car [winNames -f]]   
  637.     set scriptFile $path
  638.     set scriptStart 1
  639.     perlExecuteFile $path
  640. }
  641.  
  642. #############################################################################
  643. # Run a preattached Perl text-filter script selected from the menu:
  644. #
  645. proc textFiltersProc {menu name} {
  646.     global perlFilters scriptFile scriptStart
  647.     
  648.     perlFileAsFilter $perlFilters($menu:$name)
  649. }
  650.  
  651. #############################################################################
  652. # Reuse the previous (buffer or file) filter:
  653. #
  654. proc repeatLastFilter {} {
  655.     global scriptFile scriptStart perlPrevScript perlMenu PerlmodeVars
  656.     if {$perlPrevScript != {}} {
  657.         set stype [lindex $perlPrevScript 0]
  658.         set name [lindex $perlPrevScript 1]
  659.         if {$stype == "file"} {
  660.             perlFileAsFilter $name
  661.         } elseif {$stype == "buffer"} {
  662.             perlBufferAsFilter $name
  663.         } else {
  664.             message "Bogus filter name : \"$perlPrevScript\""
  665.             set perlPrevScript {}
  666.             set PerlmodeVars(perlLastFilter) $perlPrevScript 
  667.             enableMenuItem $perlMenu repeatLastFilter 0
  668.         }
  669.     }
  670. }
  671.  
  672. #############################################################################
  673. # Ask for a file containing a Perl script to use as a filter:
  674. #
  675. proc selectFileAsFilter {} {
  676.     global scriptFile scriptStart perlPrevScript
  677.     if {! [catch {getfile "Select a MacPerl script"} path]} {
  678.         perlFileAsFilter $path
  679.     }
  680. }
  681.  
  682. #############################################################################
  683. # Ask for an Alpha buffer containing a Perl script to use as a filter:
  684. #
  685. proc selectBufferAsFilter {} {
  686.     global scriptFile scriptStart perlPrevScript
  687.     
  688.     set windows [winNames]
  689.     set current [lindex $windows 0]
  690.     if {[llength $windows] > 1} {
  691.         set name [listpick [lsort $windows]]
  692.         if {[string length $name]} {
  693.             # get the full name of the chosen window
  694.             set wname [lindex [winNames -f] [lsearch -exact $windows $name]]
  695.             perlBufferAsFilter $wname
  696.            }
  697.     }
  698. }
  699.  
  700. #############################################################################
  701. # Open a file from the MacPerl application folder - used by "Open Special"
  702. #
  703. proc perlOpenFile {menu name} {
  704.     set filename [macperlFolder]$name
  705.     if {[file exists $filename]} {
  706.         edit $filename
  707.     } else {
  708.         alertnote "That file doesn't exist yet"
  709.     }
  710. }
  711.  
  712. #############################################################################
  713. # Support procs
  714. #############################################################################
  715.  
  716. #############################################################################
  717. # Prompt the user to enter a string containing command-line args.
  718. #
  719. proc getCmdlineArgs {} {
  720.     global PerlmodeVars
  721.     set oldargs $PerlmodeVars(perlCmdlineArgs)
  722.     if {![catch {prompt "Command-line arguments (if any):" $oldargs} args]} {
  723.         set PerlmodeVars(perlCmdlineArgs) $args
  724.     } else {
  725.         error "getCmdlineArgs: User cancelled"
  726.     }
  727.     return $args
  728. }
  729.  
  730. #############################################################################
  731. # Tell MacPerl to run a script file:
  732. #
  733. proc perlExecuteFile {path {args {}} {flags {}}} {
  734.     global ALPHA
  735.     global perlGetOutput perlAutoSwitch perlPromptArgs perlUseDebug
  736.     global scriptFile scriptStart filterHeadLen perlName
  737.     
  738.     if {[string length $path]} {
  739.         set perlName [file tail [launchBackAppl McPL]]
  740.         if {[string length $perlName]} {
  741.                 
  742.             set ok [regexp {(.*):([^:]*)} $path pathname dirname filename]
  743.             if {!$ok} {    set name $wname    }
  744.  
  745.             if {$path != [scriptPath]} {    
  746.                 set filterHeadLen 0    
  747.             }
  748.             
  749.             if {$perlUseDebug} {
  750.                 append flags "debug"
  751.             }
  752.             if {$perlPromptArgs} { 
  753.                 append args " [getCmdlineArgs]"
  754.             }
  755.             
  756.             sendCloseWinName $perlName $perlName
  757.             sendCloseWinName $perlName "Perl Debug"
  758.             if {$perlAutoSwitch || $perlUseDebug} then {
  759.                 switchTo $perlName
  760.             } else {
  761.                 message "Running file \"$filename\" as Perl script"
  762.                 watchCursor
  763.             }
  764.             
  765.             perlDoScript $perlName $path $args {} $flags
  766.             
  767. # (not sure which choice is better...)
  768. #            if {!$perlAutoSwitch} then {switchTo $ALPHA}
  769.             switchTo $ALPHA
  770. #
  771.             if {![getMacPerlError]} {
  772.                 if {$perlGetOutput} then {openPerlOutput}
  773.             }
  774.         } else {
  775.             alertnote "Couldn't run MacPerl"
  776.         }
  777.     } else {
  778.         alertnote "No file specified to execute"
  779.     }
  780. }
  781.  
  782. #############################################################################
  783. # Run a MacPerl script, passed explicitly as a string:
  784. #
  785. # If no "#!/bin/perl" line already exists, one is preprended to the script
  786. # by wrapSelectScript, which also sets $filterHeadLen for use by 
  787. # getMacPerlError.
  788. proc perlExecuteScript {script {args ""} {flags {}} } {
  789.     global perlGetOutput perlAutoSwitch perlPromptArgs perlName
  790.     global scriptFile scriptStart filterHeadLen perlUseDebug ALPHA
  791.     
  792.     if {$script != ""} {
  793.         set script [wrapSelectScript $script]
  794.         
  795.         if {![regexp {(.*):([^:]*)} $scriptFile pathname dirname filename]} {
  796.             set filename $scriptFile 
  797.         }
  798.  
  799.         set perlName [file tail [launchBackAppl McPL]]
  800.         if {[string length $perlName]} {
  801.         
  802.             if {$perlUseDebug} {
  803.                 append flags "debug"
  804.             }
  805.             if {$perlPromptArgs} { 
  806.                 append args " [getCmdlineArgs]"
  807.             }
  808.             
  809.             sendCloseWinName $perlName $perlName
  810.             sendCloseWinName $perlName "Perl Debug"
  811.             if {$perlAutoSwitch || $perlUseDebug} then {
  812.                 switchTo $perlName
  813.             } else {
  814.                 message "Running buffer \"$filename\" as Perl script"
  815.                 watchCursor
  816.             }
  817.             
  818.             perlDoScript $perlName $script $args {} $flags
  819.             
  820.             switchTo $ALPHA
  821.  
  822.             if {![getMacPerlError]} {
  823.                 if {$perlGetOutput} then {openPerlOutput}
  824.             }
  825.         }
  826.         
  827.     } else {
  828.             alertnote "Can't run an empty script"
  829.     }
  830. }
  831.  
  832. #############################################################################
  833. # Prepare the contents of a disk file for use as a text-filter script. 
  834. # (calls perlTextFilter to actually run the script)
  835. proc perlFileAsFilter {path} {
  836.     global scriptFile scriptStart perlPrevScript perlMenu PerlmodeVars
  837.     
  838.     regexp {(.*):([^:]*)} $path pathname dirname name
  839.     
  840.     if {![catch {readFile $path} coreScript]} {
  841.         set scriptFile $path
  842.         set scriptStart 1
  843.         set script [wrapFilterScript $coreScript]
  844.         set perlPrevScript [list "file" $path]
  845.         set PerlmodeVars(perlLastFilter) $perlPrevScript 
  846.         enableMenuItem $perlMenu repeatLastFilter 1
  847.         message "Running file \"$name\" as text filter ..."
  848.         
  849.         perlTextFilter $script
  850.     } else {
  851.         set perlPrevScript {}
  852.         set PerlmodeVars(perlLastFilter) $perlPrevScript 
  853.         enableMenuItem $perlMenu repeatLastFilter 0
  854.         
  855.         alertnote "Couldn't read the script file : $path"
  856.         return
  857.     }
  858. }
  859.  
  860. #############################################################################
  861. # Prepare the contents of a text window for use as a text-filter script. 
  862. # (calls perlTextFilter to actually run the script)
  863. proc perlBufferAsFilter {wname} {
  864.     global scriptFile scriptStart perlPrevScript perlMenu PerlmodeVars perlName
  865.  
  866.     set ok [regexp {(.*):([^:]*)} $wname pathname dirname name]
  867.     if {!$ok} {    set name $wname    }
  868.     
  869.     if {[lsearch [winNames -f] $wname] >= 0} {
  870.         set coreScript [getText -w $wname 0 [maxPos -w $wname]]
  871.         
  872.         # Does it have any text in it?
  873.         if {[string length $coreScript]} {
  874.             set scriptFile $wname
  875.             set scriptStart 1
  876.             set script [wrapFilterScript $coreScript]
  877.             set perlPrevScript [list "buffer" $wname]
  878.             set PerlmodeVars(perlLastFilter) $perlPrevScript 
  879.             enableMenuItem $perlMenu repeatLastFilter 1
  880.             message "Running buffer \"$name\" as text filter ..."
  881.             
  882.             perlTextFilter $script
  883.         }
  884.     } else {
  885.         set perlPrevScript {}
  886.         set PerlmodeVars(perlLastFilter) $perlPrevScript 
  887.         enableMenuItem $perlMenu repeatLastFilter 0
  888.  
  889.         alertnote "Couldn't find buffer : $name"
  890.     }
  891. }
  892.  
  893. #############################################################################
  894. # Run a Perl script as a command-line text filter, arranging for a text
  895. # buffer to be attached as standard input.  The calling routine should already
  896. # have processed the script with wrapFilterScript.  This routine actually
  897. # send the script and takes care of writing the input and reading the output 
  898. # files.
  899. proc perlTextFilter {script {args {}} {flags {}}} {
  900.     global perlOverwrite perlUsebuffer perlPromptArgs
  901.     global filterHeadLen scriptFile scriptStart perlUseDebug ALPHA
  902.     global perlOutputWindow perlRecycleOutput perlName
  903.  
  904.     set perlName [file tail [launchBackAppl McPL]]
  905.     if {![string length $perlName]} {
  906.         alertnote "Couldn't run MacPerl"
  907.         error "Couldn't run MacPerl"
  908.     }
  909.     writeStdin
  910.  
  911.     if {$perlUseDebug} {
  912.         append flags "debug"
  913.     }
  914.     if {$perlPromptArgs} { 
  915.         append args " [getCmdlineArgs]"
  916.     }
  917.     
  918.     sendCloseWinName $perlName $perlName
  919.     sendCloseWinName $perlName "Perl Debug"
  920.     
  921.     if {$perlUseDebug} then {
  922.         switchTo $perlName
  923.         perlDoScript $perlName [scriptPath] $args [list [stdinPath]] $flags
  924.         set err [getMacPerlError]
  925.  
  926.     } else {
  927.         watchCursor
  928.         set reply [perlDoScriptBatch $perlName [scriptPath] $args [list [stdinPath]]]
  929.         set err [getBatchError $reply]
  930.     }
  931.     
  932.     switchTo $ALPHA
  933.     
  934.     if {$err == 0} {
  935.         if {$perlUseDebug} {
  936.             set outp [sendGetText $perlName $perlName]
  937.         } else {
  938. #            set outp [parseReplyOutp $reply]
  939.             set outp [parseReplyResult $reply]
  940.         }
  941.         pasteFilterResult $outp
  942.     }
  943. }
  944.  
  945.  
  946. #############################################################################
  947. # Check the MacPerl output window for error messages.
  948. #
  949. proc getMacPerlError {} {
  950.     
  951.     set diag [getPerlDiag 40]
  952.     set errf [parseDiagErrf $diag]
  953.     set srcs [parseDiagSrcs $diag]
  954.     set mesg [parseDiagMesg $diag]
  955.  
  956.     if {[string length $errf]} {
  957.         showPerlDiag $diag [string length $diag] $mesg $errf $srcs
  958.         gotoPerlError $errf $srcs $mesg
  959.         return 1
  960.         
  961.     } else {
  962.         return 0
  963.     }
  964. }
  965.  
  966. #############################################################################
  967. # Check the MacPerl batch reply for error messages.
  968. #
  969. proc getBatchError {reply} {
  970.     global perlErrorWindow
  971.     
  972.     set fatalError 0
  973.     set diag [parseReplyDiag $reply]
  974.     set errf [parseDiagErrf  $diag ]
  975.     set srcs [parseReplySrcs $reply]
  976.     set mesg [parseDiagMesg  $diag ]
  977.     set errn [parseReplyErrn $reply]
  978.  
  979.     if {$errn} {        
  980.         showPerlDiag $diag $errn $mesg $errf $srcs
  981.         gotoPerlError $errf $srcs $mesg
  982.         set fatalError 1
  983.         
  984.     } elseif {[string length $diag] > 0} {
  985.         showPerlDiag $diag $errn $mesg $errf $srcs
  986.     }
  987.     
  988.     return $fatalError
  989. }
  990.  
  991. #############################################################################
  992. # Display the Perl diagnostic output in its own window.
  993. #
  994. proc showPerlDiag {diag {errn 1} {mesg {}} {errf {}} {srcs {}}} {
  995.         global perlErrorWindow    
  996.         
  997.         set currWin [lindex [winNames] 0]
  998.         if {[lsearch [winNames] $perlErrorWindow] >= 0} {
  999.             bringToFront $perlErrorWindow
  1000.             setWinInfo read-only 0
  1001.             deleteText 0 [maxPos] 
  1002.             insertText $diag
  1003.         } else {
  1004.             new -n $perlErrorWindow 
  1005.              insertText $diag
  1006.         }
  1007.         
  1008.         goto 0
  1009.         catch {shrinkWindow 2}
  1010.         setWinInfo dirty 0
  1011.         setWinInfo read-only 1
  1012.         bringToFront $currWin
  1013. }
  1014.  
  1015. #############################################################################
  1016. # Bring up a window containing the bug-ridden Perl code and highlight the
  1017. # line at which the error was found.
  1018. #
  1019. proc gotoPerlError {errf srcs {mesg {}}} {
  1020.     global scriptFile scriptStart filterHeadLen
  1021.  
  1022.     if {$errf == [scriptPath] || $errf == "<AppleEvent>"} {
  1023.         set errf $scriptFile
  1024.         # Convert it to the line number in the original file
  1025.         set srcs [expr $srcs + $scriptStart - $filterHeadLen - 1]
  1026.     }
  1027.     # ... and leave an informative error message
  1028.     #
  1029.     if {[string length $mesg]} {
  1030.         set mesg "$mesg at Line $srcs"            
  1031.     } else {
  1032.         set mesg "MacPerl flagged an error at Line $srcs"    
  1033.     }
  1034.     
  1035.     # Bring up the script file and highlight the flagged line
  1036.     #
  1037.     catch {gotoFileLine $errf $srcs $mesg} fname    
  1038. }
  1039.  
  1040. #############################################################################
  1041. # Read the first block of lines (up to a maximum number) from the MacPerl
  1042. # output window.
  1043. #
  1044. proc getPerlDiag {maxlines} {
  1045.     global perlName
  1046.     set pat0 {^[ \t]*$}
  1047.  
  1048.     set lines {}    
  1049.  
  1050.     # read first $maxlines of output to the MacPerl window
  1051.     # (faster, but assumes error message won't appear at 
  1052.     # the end of a lot of output).
  1053.     #
  1054.     set nlines [sendCountLines $perlName MacPerl]
  1055.     set nlines [expr ($nlines > $maxlines)?$maxlines:$nlines]
  1056.     if {$nlines > 0} {
  1057.         set output [sendGetText $perlName $perlName 1 $nlines]
  1058.         
  1059.         foreach line [split $output "\r"] {
  1060.             if  {[regexp $pat0 $line mtch]} {
  1061.                 break
  1062.             } else {
  1063.                 append lines "$line\n"
  1064.             }
  1065.         }
  1066.     }
  1067.     return $lines
  1068. }
  1069.  
  1070. #############################################################################
  1071. # Extract various items out of the MacPerl diagnostic output
  1072. #
  1073.  
  1074. # Name of the file in which the error was found
  1075. #
  1076. proc parseDiagErrf {diag}    {
  1077.     if {![regexp {File '([^']+)'; Line} $diag allofit errf]} { 
  1078.         set errf {}
  1079.     }
  1080.     return $errf
  1081. }
  1082.  
  1083. # The line number on which the error was found
  1084. #
  1085. proc parseDiagSrcs {diag}    {
  1086.     if {![regexp {File '[^']+'; Line ([0-9]+)} $diag allofit srcs]} { 
  1087.         set srcs 0 
  1088.     }
  1089.     return $srcs
  1090. }
  1091.  
  1092. # The error message associated with error
  1093. #
  1094. proc parseDiagMesg {diag} {
  1095.     set pat1 {^#(.*)$}
  1096.     set pat2 {File '([^']+)'; Line ([0-9]+)}
  1097.     
  1098.     set errMessage {}
  1099.     set errFound 0
  1100.     
  1101.     foreach line [split $diag "\n"] {
  1102.         if {[regexp $pat2 $line mtch num]} {
  1103.             set errFound 1
  1104.         } elseif {[regexp $pat1 $line mtch err]} {
  1105.             if {$errFound == 0} {
  1106.                 set errMessage $err
  1107.             }
  1108.         }
  1109.     }
  1110.     return $errMessage
  1111. }
  1112.  
  1113. #############################################################################
  1114. # Extract various return parameters out of a MacPerl DoScript reply
  1115. #
  1116.  
  1117. # Result from batch script
  1118. #
  1119. proc parseReplyResult {reply} {
  1120.     if {![regexp {'?\-\-\-\-'?:“([^”]*)”} $reply allofit result]} { 
  1121.         set result {}
  1122.     }
  1123.     return $result
  1124. }
  1125.  
  1126. # Standard output of batch script
  1127. #
  1128. proc parseReplyOutp {reply} {
  1129.     if {![regexp {OUTP:“([^”]*)”} $reply allofit outp]} { 
  1130.         set outp {}
  1131.     }
  1132.     return $outp
  1133. }
  1134.  
  1135. # Diagnostic output of the batch script
  1136. #
  1137. proc parseReplyDiag {reply}    {
  1138.     if {[regexp {diag:“([^”]*)”} $reply allofit diag]}  {
  1139.     } else { 
  1140.         set diag {}
  1141.     }
  1142.     return $diag
  1143. }
  1144.  
  1145. # File alias of the script file in which the error was found
  1146. #
  1147. proc parseReplyErob {reply}    {
  1148.     if {![regexp {erob:alis\(«(.*)»\)} $reply allofit erob]} {
  1149.         set erob {} 
  1150.     }
  1151.     return $erob
  1152. }
  1153.  
  1154. # First line flagged in error
  1155. #
  1156. proc parseReplySrcs {reply}    {
  1157.     if {![regexp {erng:{srcs:([0-9]+)[^\}]*}} $reply allofit srcs]} { 
  1158.         set srcs 0 
  1159.     }
  1160.     return $srcs
  1161. }
  1162.  
  1163. # Last line flagged in error
  1164. #
  1165. proc parseReplySrce {reply}    {
  1166.     if {![regexp {erng:{[^\}]*srce:([0-9]+)}} $reply allofit srce]} { 
  1167.         set srce 0
  1168.     }
  1169.     return $srce
  1170. }
  1171.  
  1172. # Error number
  1173. #
  1174. proc parseReplyErrn {reply}    {
  1175.     if {![regexp {errn:([0-9]+)} $reply allofit errn]} {
  1176.         set errn 0
  1177.     }
  1178.     return $errn
  1179. }
  1180.  
  1181. #############################################################################
  1182. #  Take a Perl script and add commands to take the file STDIN as standard
  1183. #  input and STDOUT as standard output.  This allows scripts written as
  1184. #  Unix command-line filters to be used in the (non-MPW) Mac environment as
  1185. #  text filters.
  1186. #
  1187. #  If there's already a #! line in the script, then the new commands
  1188. #  are added after that line.  If there was no #! line in the first place,
  1189. #  one is added, in case MacPerl is set up to require it (can't hurt...) 
  1190. #
  1191. #  $filterHeadLen counts the number of lines we add to the top of the
  1192. #  original script, so that we can allow for it in interpreting error
  1193. #  messages issued by MacPerl.
  1194. #
  1195. #  *** As of MacPerl 4.1.4, this business is pretty much obsolete ***
  1196. #
  1197. proc wrapFilterScript {coreScript} {
  1198.     global scriptStart filterHeadLen interpPat
  1199.  
  1200.     if {[regexp -indices $interpPat $coreScript allofit cmdln]} {
  1201.         set endPos [lindex $cmdln 1]
  1202.         set filterHead [string range $coreScript 0 [expr $endPos+1]]
  1203.         set coreScript [string range $coreScript [expr $endPos+2] end]
  1204.         set filterHeadLen 0
  1205.         incr scriptStart [expr [llength [split $filterHead "\n\r"]] -2]
  1206.     } else {
  1207.         set filterHead "#!/bin/perl\r\n"
  1208.         set filterHeadLen 2
  1209.     }
  1210.         
  1211.     set script $filterHead
  1212.     append script $coreScript
  1213.     
  1214.     # for debugging purposes, save the script on disk
  1215.     #
  1216.     writeScript $script
  1217.     return $script
  1218. }        
  1219.  
  1220. #############################################################################
  1221. #  Add a #!/bin/perl line to the script if it doesn't contain one already.
  1222. #  (MacPerl puts up dialog if this line is missing when it expects it,
  1223. #  hanging the DoScript and leaving us stuck.)
  1224. #
  1225. proc wrapSelectScript {coreScript} {
  1226.     global scriptStart filterHeadLen interpPat
  1227.  
  1228.     if {[regexp -indices $interpPat $coreScript allofit cmdln]} {
  1229.         set endPos [lindex $cmdln 1]
  1230.         set filterHead [string range $coreScript 0 [expr $endPos+1]]
  1231.         set script $coreScript
  1232.         set filterHeadLen 0
  1233.         incr scriptStart [expr [llength [split $filterHead "\n\r"]] -2]
  1234.     } else {
  1235.         set script "#!/bin/perl\r\n"
  1236.         append script $coreScript
  1237.         set filterHeadLen 1
  1238.     }
  1239.     
  1240.     # for debugging purposes, save the script on disk
  1241.     #
  1242.     writeScript $script
  1243.     return $script
  1244. }        
  1245.  
  1246. #############################################################################
  1247. #  Paste result of the filter operation in place of the input text, or in
  1248. #  a new window (depending on the flag $perlOverwrite
  1249. #
  1250. proc pasteFilterResult {text} {
  1251.     global perlOverwrite perlRecycleOutput perlOutputWindow
  1252.     global perlUsebuffer 
  1253.     
  1254.     if {!$perlOverwrite} {
  1255.         if {$perlRecycleOutput && 
  1256.             [lsearch [winNames] $perlOutputWindow] >= 0} {                
  1257.             bringToFront $perlOutputWindow
  1258.         } else {
  1259.             new -n $perlOutputWindow
  1260.         }
  1261.     }
  1262.     
  1263.     if {$perlUsebuffer || $perlRecycleOutput} {
  1264.         set from 0
  1265.         set to [maxPos]
  1266.     } else {
  1267.         set from [getPos] 
  1268.         set to [selEnd]
  1269.     }    
  1270.     replaceText $from $to $text
  1271.     
  1272.     if {!$perlOverwrite || $perlUsebuffer} {
  1273.         catch {shrinkWindow 2}
  1274.         goto 0
  1275.     } else {
  1276.         catch shrinkWindow
  1277.         goto $from
  1278.     }
  1279.     if {!$perlOverwrite} { setWinInfo dirty 0 }
  1280. }    
  1281.  
  1282. #############################################################################
  1283. #  Extend the current selection to encompass complete lines.  If the 
  1284. #  'applyToBuffer' flag is checked, then the entire buffer is selected.
  1285. #
  1286. proc completeSelection {} {
  1287.     global perlUsebuffer filterInput
  1288.     set filterInput "buffer \"[lindex [winNames] 0]\""
  1289.     if {$perlUsebuffer} {
  1290.         set start 0
  1291.         set end [maxPos]
  1292.     } else {
  1293.         set start [lineStart [getPos]]
  1294.         set end [nextLineStart [expr [selEnd]-1]]
  1295.         if {$end == $start} { set end [nextLineStart [selEnd]] }
  1296.         
  1297.         set startLine [lindex [posToRowCol $start] 0]
  1298.         set endLine [expr [lindex [posToRowCol $end] 0] - 1]
  1299.         if {$endLine > $startLine+1} {
  1300.             set filterInput "lines $startLine to $endLine of $filterInput"
  1301.         } else {
  1302.             set filterInput "line $startLine of $filterInput"
  1303.         }
  1304.    }
  1305.     return [list $start $end]
  1306. }
  1307.  
  1308. #############################################################################
  1309. #  writeStdin: Extend the selection, as appropriate, and write it to the 
  1310. #     STDIN file in the MacPerl directory.
  1311. #
  1312. #  writeScript: Write the SCRIPT file in the MacPerl directory.  MacPerl will
  1313. #     read the script from this file. 
  1314. #
  1315. proc writeStdin {} {
  1316.     set res [completeSelection]
  1317.     set tmpfid [open [stdinPath] "w+"]
  1318.     puts $tmpfid [eval getText $res]
  1319.     close $tmpfid
  1320. }
  1321.  
  1322. # This is unnecessary now, but maybe it'll still useful to save the script
  1323. # file for debugging.
  1324. #
  1325. proc writeScript {script} {
  1326.     set tmpfid [open [scriptPath] "w+"]
  1327.     puts $tmpfid $script 
  1328.     close $tmpfid
  1329. }
  1330.  
  1331. #############################################################################
  1332. # Read the MacPerl output window and load the contents, if any, into
  1333. # a new Alpha window. 
  1334. #
  1335. proc openPerlOutput {} {
  1336.     global perlRecycleOutput perlOutputWindow perlName
  1337.     
  1338.     set output [sendGetText $perlName $perlName]
  1339.     if {[string length $output]} {
  1340.         if {$perlRecycleOutput && 
  1341.             [lsearch [winNames] $perlOutputWindow] >= 0} {
  1342.             
  1343.             bringToFront $perlOutputWindow
  1344.             replaceText 0 [maxPos] $output
  1345.         } else {
  1346.             new -n $perlOutputWindow
  1347.             insertText $output
  1348.         }
  1349.         catch {shrinkWindow 2}
  1350.         setWinInfo dirty 0
  1351.         goto 0
  1352.     }
  1353. }
  1354.  
  1355. #############################################################################
  1356. # translate special DoScript flags into flags string $usrf
  1357. #
  1358. proc perlScriptFlags {{flags {}}} {
  1359.      set usrf {}
  1360.  
  1361.     if {[lsearch -exact $flags "extract"] >= 0} {
  1362.         append usrf { "EXTR" 'true'}
  1363.     } elseif {[lsearch -exact $flags "noextract"] >= 0} {
  1364.         append usrf { "EXTR" 'fals'}
  1365.     }        
  1366.     if {[lsearch -exact $flags "debug"] >= 0} {
  1367.         append usrf { "DEBG" 'true'}
  1368.     } elseif {[lsearch -exact $flags "nodebug"] >= 0} {
  1369.         append usrf { "DEBG" 'fals'}
  1370.     }        
  1371.  
  1372.     if {[lsearch -exact $flags "local"] >= 0} {
  1373.         append usrf { "MODE" 'LOCL'}
  1374.     } elseif {[lsearch -exact $flags "batch"] >= 0} {
  1375.         append usrf { "MODE" 'BATC'}
  1376.     } elseif {[lsearch -exact $flags "remote"] >= 0} {
  1377.         append usrf { "MODE" 'RCTL'}
  1378.     }        
  1379.     return $usrf
  1380.  
  1381. proc perlScriptArgs {{args {}} {fileargs {}}} {
  1382.     set nargs 0
  1383.     set argv {}
  1384.     
  1385.     foreach item [parseWords $args] {
  1386.         set item [string trim $item]
  1387.         if {[string length $item]} {
  1388.             append argv ", [curlyq $item]"
  1389.             incr nargs
  1390.         }
  1391.     }
  1392.     foreach filename $fileargs {
  1393.         set item [string trim $filename]
  1394.         if {[string length $item]} {
  1395.             append argv ", [curlyq $item]"
  1396.             incr nargs
  1397.         }
  1398.     }
  1399.     return $argv
  1400. }
  1401.  
  1402. #############################################################################
  1403. # General Apple Event routines
  1404. # (most of these have been moved to Modes:appleEvents.tcl)
  1405. #
  1406. # DoScript for MacPerl 4.1.3
  1407. # (runs in "Local" mode under v4.1.4+)
  1408. #
  1409. proc perlDoScript {appname script {args {}} {fileargs {}} {flags {}} } {
  1410.     # form list of quoted "command-line" args
  1411.     #
  1412.     if {$script != ""} {
  1413.         set argv "\[[curlyq [string trim $script]]"
  1414. #         foreach item [split [join $args " "] " "] {
  1415. #}
  1416.         append argv [perlScriptArgs $args $fileargs]
  1417.         append argv "]"
  1418.         
  1419.         set usrf [perlScriptFlags $flags]
  1420.         set reply [eval "AEBuild -t 36000 -r \"$appname\" misc dosc $usrf \"----\" [list $argv] "]
  1421.     #    alertnote $reply
  1422.     }
  1423. }
  1424.  
  1425. # DoScript for MacPerl 4.1.4+
  1426. #
  1427. proc perlDoScriptBatch {appname script {args {}} {fileargs {}}} {
  1428.     
  1429.     # form list of quoted "command-line" args
  1430.     #
  1431.     if {$script != ""} {
  1432.         set argv "\[[curlyq [string trim $script]]"
  1433.         append argv [perlScriptArgs $args $fileargs ] 
  1434.         append argv "]"
  1435.                 
  1436.         set reply [eval "AEBuild -t 36000 -r \"$appname\" misc dosc MODE BATC \"----\" [list $argv]"]
  1437.         
  1438. #         perlDisplayReply $reply
  1439.  
  1440.     } else {
  1441.         set reply {}
  1442.     }
  1443.     return $reply
  1444. }
  1445.  
  1446. # For debugging 
  1447. #
  1448. proc perlDisplayReply {reply} {
  1449.     set currWin [lindex [winNames] 0]
  1450.     new -n {*** DoScript Reply **} 
  1451.     insertText $reply
  1452.         
  1453.     goto 0
  1454.     catch {shrinkWindow 2}
  1455.     setWinInfo dirty 0
  1456.     setWinInfo read-only 1
  1457.     bringToFront $currWin
  1458. }
  1459.  
  1460. # DoScript to launch interactive debugger (for MacPerl 4.1.4+)
  1461. #
  1462. proc perlDoScriptDebug {appname script {args {}} {fileargs {}}} {
  1463.     
  1464.     # form list of quoted "command-line" args
  1465.     #
  1466.     if {$script != ""} {
  1467.         set argv "\[[curlyq [string trim $script]]"
  1468.         append argv [perlScriptArgs "$args debug" $fileargs ] 
  1469.         append argv "]"
  1470.                 
  1471.         set reply [eval "AEBuild -t 36000 -r \"$appname\" misc dosc MODE RCTL \"----\" [list $argv]"]
  1472.  
  1473.         new -n {** DoScriptDebug Reply **} 
  1474.         insertText $reply
  1475.             
  1476.         goto 0
  1477.         catch {shrinkWindow 2}
  1478.         setWinInfo dirty 0
  1479.         setWinInfo read-only 1
  1480.  
  1481.  
  1482.     } else {
  1483.         set reply {}
  1484.     }
  1485.     return $reply
  1486. }
  1487.  
  1488. ##############################################################################
  1489. # Automatic indexing of Perl subs
  1490. #
  1491. proc PerlMarkFile {} {
  1492.     set end [maxPos]
  1493.     set pos 0
  1494.     set l {}
  1495.     while {![catch {search -f 1 -r 1 -m 0 -i 0 {^sub} $pos} res]} {
  1496.         set start [lindex $res 0]
  1497.         set end [nextLineStart $start]
  1498.         set text [lindex [getText $start $end] 1]
  1499.         set pos $end
  1500.         set inds($text) [lineStart [expr $start - 1]]
  1501.     }
  1502.  
  1503.     if {[info exists inds]} {
  1504.         foreach f [lsort [array names inds]] {
  1505.             set next [nextLineStart $inds($f)]
  1506.             setNamedMark $f $inds($f) $next $next
  1507.         }
  1508.     }
  1509. }
  1510.  
  1511.  
  1512. # Open a 'require'd Perl file.
  1513. proc perlFindRequire {from {to 0}} {
  1514.     set reqPat {^[     ]*require[     ]*(\"[^\"]+\"|\'[^\']+\'|[^     ]+)}
  1515.     if {$to == 0} { set to $from }
  1516.     set beg [lineStart $from]
  1517.     set end [nextLineStart $to]
  1518.     set words [parseWords [getText $beg $end]]
  1519.     if {[string tolower [lindex $words 0]] != "require"} {
  1520.         error "Not a require statement"
  1521.     }
  1522.     set root [string trim [lindex $words 1] {'"}]
  1523.     return $root
  1524. }
  1525.  
  1526. proc inlineRequires {} {
  1527.     global lastMatchingLines
  1528.     
  1529.     set reqPat {^[     ]*require[     ]*(\"[^\"]+\"|\'[^\']+\'|[^     ]+)}
  1530.     set pos 0
  1531.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $reqPat $pos} mtch]} {
  1532.          [lindex [posToRowCol [lindex $mtch 0]] 0]] 
  1533.         set name [string [eval getText $mtch]
  1534.         set pos [lindex $mtch 1]
  1535.         incr matches
  1536.     }
  1537. }
  1538.  
  1539. # Open a Perl source file. 
  1540. #
  1541. proc openPerlFile {file {extensions {""}}} {
  1542.     global perlSearchPath
  1543.     # Determine absolute file specification
  1544.     # Ignore $extensions if $file already has an extension
  1545.     if {[string length [file extension $file]] == 0} {
  1546.         set extensions {""}
  1547.     }
  1548.     foreach ext $extensions {
  1549.         set filename [absolutePath $file$ext]
  1550.         if {![catch {openFileQuietly $filename}]} {
  1551.             message $filename
  1552.             return 
  1553.         }
  1554.     }
  1555.     if {[llength $perlSearchPath] == 0} { buildPerlSearchPath }
  1556.     foreach folder $perlSearchPath {
  1557.         foreach ext $extensions {
  1558.             set filename "$folder$file$ext"
  1559.             if {![catch {openFileQuietly $filename}]} {
  1560.                 message $filename
  1561.                 return     
  1562.             }
  1563.         }
  1564.     }
  1565.     beep
  1566.     message "can't find Perl source file \"$file\""
  1567. }
  1568.  
  1569. # Return a list of folders in which to search for Perl library files, 
  1570. # including the lib folder in the Perl application directory and the
  1571. # $perlLib folder (if it exists) .  
  1572. # The current folder is not included in the list.
  1573. #
  1574. # (The $perlLib folder is assigned from the AppPaths submenu.)
  1575. #
  1576. proc buildPerlSearchPath {} {
  1577.     global perlLib perlSearchPath
  1578.     message "building Perl search path..."
  1579.     set folders {}
  1580.     
  1581.     # The local lib folder:
  1582.     if {[info exists perlLib] && [string length $perlLib] > 0} { 
  1583.         set folders [concat $folders [list $perlLib]]
  1584.         # Search subfolders one level deep:
  1585.         set folders [concat $folders [listSubfolders $perlLib 1]]
  1586.     }
  1587.  
  1588.     # Any "*lib*" folders in the MacPerl application folder:
  1589.     set macperlPath [nameFromAppl McPL]
  1590.     set appDir [file dirname $macperlPath]
  1591.     set folders [concat $folders [list $appDir]]
  1592.     # Bug:  'glob' is case sensitive!
  1593.     foreach folder [glob "$appDir:*\[Ll\]ib*"] {
  1594.         set folders [concat $folders [list $folder]]
  1595.         # Search subfolders one level deep:
  1596.         set folders [concat $folders [listSubfolders $folder 1]]
  1597.     }
  1598.  
  1599.     # Make sure each folder ends with a colon
  1600.     set perlSearchPath {}
  1601.     foreach folder $folders {
  1602.         set folder "[string trimright $folder {:}]:"
  1603.         set perlSearchPath [concat $perlSearchPath [list $folder]]
  1604.     }
  1605. }
  1606.  
  1607. ###########################################################################
  1608. #
  1609. proc perlHelpProc {menu item} {
  1610.     global HOME perlDocs
  1611.     switch $item {
  1612.         "MacPerl Mode"    {
  1613.                 if {[catch {openFileQuietly "$HOME:Help:MacPerl Help"}]} {
  1614.                     alertnote "File not found:\r$HOME:Help:MacPerl Help"
  1615.                 }
  1616.             }
  1617.         "Mac Specifics"    {
  1618.                 if {[catch {openFileQuietly "$HOME:Help:MacPerl.Specifics"}]} {
  1619.                     alertnote "File not found:\r$HOME:Help:MacPerl.Specifics"
  1620.                 }
  1621.             }
  1622.         "Perl4 Manual"    {
  1623.                 if {[catch {openFileQuietly "$HOME:Help:Perl Commands"}]} {
  1624.                     alertnote "File not found:\r$HOME:Help:Perl Commands"
  1625.                 }
  1626.             }
  1627.         "Perl5 Manual"    {
  1628.                 catch {editMark "$HOME:Help:Perl Commands" Perl5 -r}
  1629.             }
  1630.     }
  1631. }
  1632.  
  1633. proc electricPerlLeft {} {
  1634.     set prevChar [lookAt [expr [getPos] - 1]]
  1635.     if {$prevChar == " " || $prevChar == "\)"} {
  1636.         electricLeft
  1637.         return
  1638.     }
  1639.     deleteText [getPos] [selEnd]
  1640.     insertText "\{"
  1641. }
  1642.  
  1643. proc electricPerlRight {} {
  1644.     set prevChar [lookAt [expr [getPos] - 1]]
  1645.     if {$prevChar == " " || $prevChar == ";" || $prevChar == "\t" || $prevChar == "\}"} {
  1646.         electricRight
  1647.         return
  1648.     }
  1649.     deleteText [getPos] [selEnd]
  1650.     insertText "\}"
  1651.     catch {blink [matchIt "\}" [expr [getPos]-2]]}
  1652.     return
  1653. }
  1654.  
  1655. bind '\r' tclCarriageReturn Perl
  1656. bind '\}' <s> electricPerlRight Perl
  1657. bind '\{' <s> electricPerlLeft Perl
  1658. bind '\;' electricSemi Perl
  1659. bind '\t' <z> doATab Perl
  1660.  
  1661. #
  1662.  
  1663.